
#include <stdint.h>
#include <stdbool.h>
#include "user_bt.h"
#include "assert.h"
#include "app_task.h"
#include "FreeRTOS.h"
#include "timers.h"
#include "btdm_mem.h"

#define MAX_RECONNECT_TIMES 3   //reconnect times if page timeout or linkloss
#define PAGE_TIMEOUT    0x1000   //connect timeout value, uint:625us
#define MAX_NAME_SIZE   24
#define SCO_DATA_BUFFER_COUNT       3

///enable audio transfer, audio is transfered from phone to earphone
#define ENABLE_AUDIO_TRANSFER

#ifdef ENABLE_AUDIO_TRANSFER
uint8_t voice_phone_data[120];
uint8_t voice_headset_data[120];
#endif

struct sco_data_t {
    struct co_list_hdr hdr;
    void *arg;
    uint16_t length;
    uint8_t data[];
};

struct sbc_info_t{
    uint8_t elem[4];
    SbcStreamInfo info;
};

const uint8_t hf_enable_voltage_notify[] = "AT+XAPL=AAAA-1111-01,10";
const uint8_t hf_search_remote_dev_type[] = "AT+CGMI";

struct user_bt_env_t user_bt_env;
static TimerHandle_t bt_connect_timer = NULL;
static TimerHandle_t bt_avrcp_connect_timer = NULL;
static TimerHandle_t bt_a2dp_connect_timer = NULL;
static TimerHandle_t bt_linkloss_connect_timer = NULL;

struct sbc_info_t sbcinfo[NUM_BT_DEVICES];
static void bt_save_codec_info(uint8_t dev_index, uint8_t *elements)
{
    memcpy(&sbcinfo[dev_index].elem[0], elements, 4);
    /* copy codec info to local*/
    sbcinfo[dev_index].info.bitPool = elements[3];
    if(elements[0]&0x20){
        sbcinfo[dev_index].info.sampleFreq = 2;//44100
    }
    else{
        sbcinfo[dev_index].info.sampleFreq = 3;//48000
    }
    sbcinfo[dev_index].info.allocMethod = elements[1]&0x03; //1-->loudness, 2--->snr
    
    if(elements[1]&0x10){
        sbcinfo[dev_index].info.numBlocks = 16;
    }
    else if(elements[1]&0x20){
        sbcinfo[dev_index].info.numBlocks = 12;
    }
    else if(elements[1]&0x40){
        sbcinfo[dev_index].info.numBlocks = 8;
    }
    else{
        sbcinfo[dev_index].info.numBlocks = 4;
    }
    
    if(elements[1]&0x04){
        sbcinfo[dev_index].info.numSubBands = 8;
    }
    else{
        sbcinfo[dev_index].info.numSubBands = 4;
    }
}

static uint16_t sbc_calc_frame_len(struct sbc_info_t *info)
{
    uint16_t temp = (1*info->info.numSubBands) + (info->info.numBlocks*info->info.bitPool);
    uint16_t frame_len = 4 + ((4*info->info.numSubBands*info->info.numChannels)>>3) + (temp>>3);
    if(temp%8){
        frame_len++;
    }   
    return frame_len;
}

uint8_t bt_get_free_dev(void)
{
    uint8_t index;
    for(index = 0; index < NUM_BT_DEVICES; index++){
        if(user_bt_env.dev[index].state == BT_LINK_STATE_IDLE){
            break;
        }
    }
    return index;
}

uint8_t bt_find_dev_index(BD_ADDR *addr)
{
    uint8_t index;
    printf("find dev :%x,%x,,%x\r\n",addr->A[0],addr->A[1],user_bt_env.dev[0].remote_bd.A[0]);
    for(index = 0; index < NUM_BT_DEVICES; index++){
        if(memcmp(&user_bt_env.dev[index].remote_bd, addr, sizeof(BD_ADDR)) == 0){
            break;
        }
    }
    return index;
}
uint8_t bt_find_dev_index_by_dev(void *dev)
{
    uint8_t index;
    if(dev == NULL){
     return NUM_BT_DEVICES;
    }
    for(index = 0; index < NUM_BT_DEVICES; index++){
        if(user_bt_env.dev[index].remDev == dev){
            break;
        }
    }
    return index;
}

uint8_t bt_find_dev_index_by_hfchan(HfChannel *chan)
{
    uint8_t index;
    if(chan == NULL){
     return NUM_BT_DEVICES;
    }
    for(index = 0; index < NUM_BT_DEVICES; index++){
        if(user_bt_env.dev[index].hf_chan == chan){
            break;
        }
    }
    return index;
}

uint8_t bt_find_dev_index_by_hfgchan(HfgChannel *chan)
{
    uint8_t index;
    if(chan == NULL){
     return NUM_BT_DEVICES;
    }
    for(index = 0; index < NUM_BT_DEVICES; index++){
        if(user_bt_env.dev[index].hfg_chan == chan){
            break;
        }
    }
    return index;
}

uint8_t bt_find_dev_index_by_rcpchan(AvrcpChannel *chan)
{
    uint8_t index;
    if(chan == NULL){
     return NUM_BT_DEVICES;
    }
    for(index = 0; index < NUM_BT_DEVICES; index++){
        if(user_bt_env.dev[index].rcp_chan == chan){
            break;
        }
    }
    return index;
}

uint8_t bt_find_dev_index_by_pbapclient(PbapClientSession *client)
{
    uint8_t index;
    if(client == NULL){
     return NUM_BT_DEVICES;
    }
    for(index = 0; index < NUM_BT_DEVICES; index++){
        if(user_bt_env.dev[index].pbap_client == client){
            break;
        }
    }
    return index;
}

static void bt_parse_inq_result(uint8_t *input, uint8_t *name, uint8_t *len)
{
    uint8_t i = 0,k = 0;
    uint8_t *ptr = input;
    
    *len = 0;
    while(i<240){
        if(*(ptr + i) == 0){
            break;//i = 240;
        }else{
            if(*(ptr+1+i) == 0x09){
                *len = *(ptr+i);
                k = i;
                break;//i = 240;
            }else{
                i += *ptr + 1;
            }
        }
    }
    if(*len > MAX_NAME_SIZE){
        memcpy(name,input+k+2,MAX_NAME_SIZE);
    }   
    else if(*len > 0){
        memcpy(name,input+k+2,*len);
    }
}


BtStatus bt_do_connect(BD_ADDR *addr, uint8_t profiles, void **chan)
{
    BtStatus ret = BT_STATUS_FAILED;
    uint8_t index = 0;
    printf("bt do connect\r\n");
    if((profiles & user_bt_env.enable_profiles) == profiles){
        //connect to remote device depending on the profile
        if(profiles & ENABLE_PROFILE_HF){
            index = bt_get_free_hf_channel();
            if(index < NUM_BT_DEVICES){
            ret = HF_CreateServiceLink(&hf_channel[index], addr);
                *chan = (void *)&hf_channel[index];            
            }
        }
        else if(profiles & ENABLE_PROFILE_HFG){
            index = bt_get_free_hfg_channel();
            if(index < NUM_BT_DEVICES){
            ret = HFG_CreateServiceLink(&hfg_channel[index], addr);
                *chan = (void *)&hfg_channel[index]; 
            }
        }
        else if(profiles & ENABLE_PROFILE_A2DP_SINK){
            index = bt_get_free_a2dp_sink_stream();
            if(index < NUM_BT_DEVICES){
            ret = A2DP_OpenStream(&Stream[index],addr);
                *chan = (void *)&Stream[index];
            }
        }
        else if(profiles & ENABLE_PROFILE_A2DP_SOURCE){
            index = bt_get_free_a2dp_source_stream();
            if(index < NUM_STREAMS){
            ret = A2DP_OpenStream(&Stream[index],addr);
                *chan = (void *)&Stream[index];
            }
        }
        else if(profiles & ENABLE_PROFILE_HID){
        
        }
        else if(profiles & ENABLE_PROFILE_SPP){
        
        }    
    }

    return ret;
}

BtStatus bt_do_disconnect(BD_ADDR *addr, CmgrHandler *handler,uint8_t force)
{
    uint8_t dev_index;
    BtStatus status = BT_STATUS_PENDING;
    
    dev_index = bt_find_dev_index(addr);
    xTimerStop(bt_connect_timer,portMAX_DELAY);
    xTimerStop(bt_avrcp_connect_timer,portMAX_DELAY);
    xTimerStop(bt_a2dp_connect_timer,portMAX_DELAY);
    xTimerStop(bt_linkloss_connect_timer,portMAX_DELAY);
    user_bt_env.connect_times = 0;

    printf("bt do disconnect\r\n");
    if(force == true){
        if(user_bt_env.dev[dev_index].remDev != NULL){
            ///do acl disconnect
            ME_ForceDisconnectLinkWithReason(NULL,user_bt_env.dev[dev_index].remDev,BEC_USER_TERMINATED,TRUE); 
        }
        else{
//            status = ME_ForceCancelCreateLink();
            status = CMGR_RemoveDataLink(handler);
        }
        
//        if(status != BT_STATUS_PENDING){
//            ///no acl connection,just reset the state
//            for(uint8_t i = 0; i < NUM_BT_DEVICES; i++){
//                if(hf_channel[i].state == HF_STATE_CONN_PENDING){
//                    hf_channel[i].state = HF_STATE_CLOSED;
//                    hf_channel[i].flags = 0;
//                }            
//            }
//        }

    }
    else{
        if(user_bt_env.dev[dev_index].conFlags & LINK_STATUS_SCO_CONNECTED){
            HF_DisconnectAudioLink(user_bt_env.dev[dev_index].hf_chan);
         }
        if(user_bt_env.dev[dev_index].hf_chan->state != HF_STATE_CLOSED){
            HF_DisconnectServiceLink(user_bt_env.dev[dev_index].hf_chan);
        }
        if(user_bt_env.dev[dev_index].hfg_chan->state != HFG_STATE_CLOSED){
            HFG_DisconnectServiceLink(user_bt_env.dev[dev_index].hfg_chan);
        }
        if(user_bt_env.dev[dev_index].rcp_chan->chnl.conn.state != 0){
            AVRCP_Disconnect(user_bt_env.dev[dev_index].rcp_chan);
        }
        if(user_bt_env.dev[dev_index].pstream->stream.state != 0){
            A2DP_CloseStream(user_bt_env.dev[dev_index].pstream);
        }    
        if(user_bt_env.dev[dev_index].pbap_client != NULL){
            PBAP_ClientDisconnect(user_bt_env.dev[dev_index].pbap_client);
        }
    }
    return status;
}

//BtStatus bt_connect_internal(uint8_t dev_index)
//{
//    BtStatus status;
//    if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_CONNECTING){
//        if(user_bt_env.connect_param.connect_times > 0){
//            user_bt_env.connect_param.connect_times --;
//            status = bt_connect_according_to_profile(&user_bt_env.dev[dev_index].remote_bd,user_bt_env.enable_profiles);
//        }
//        else{
//            status =  BT_STATUS_TIMEOUT;
//        }
//    }
//    else{
//        status =  BT_STATUS_INVALID_PARM;
//    }
//    printf("connect internal %d,%d\r\n",user_bt_env.connect_param.connect_times,status);
//    if(status != BT_STATUS_PENDING){
//        user_bt_env.connect_param.connect_times = 0;
//        user_bt_env.dev[dev_index].state = BT_LINK_STATE_IDLE;
//        memset(&user_bt_env.dev[dev_index].remote_bd,0,sizeof(BD_ADDR));
//    }
//    return status;
//}

void bt_disconnect_cb(BD_ADDR *addr, uint8_t errcode)
{
    BtStatus status;
    if(errcode == BEC_CONNECTION_TIMEOUT){
        //linkloss, auto reconnect in hf mode
//        user_bt_env.connect_times = MAX_RECONNECT_TIMES;
//        status = bt_connect(addr);
        uint8_t dev_index = bt_find_dev_index(addr);
        user_bt_env.connect_times = MAX_RECONNECT_TIMES;
        memcpy(&user_bt_env.last_dev_addr,addr,BD_ADDR_SIZE);
        printf("linkloss>>%d,%x\r\n",dev_index,user_bt_env.dev[dev_index].connect_profile);
        xTimerChangePeriod(bt_linkloss_connect_timer, 10000, portMAX_DELAY);
        vTimerSetTimerID(bt_linkloss_connect_timer,(void *)(uint32_t)user_bt_env.dev[dev_index].connect_profile);
        xTimerStart(bt_linkloss_connect_timer,portMAX_DELAY);
    }
    else if(errcode == BEC_USER_TERMINATED){
    
    }
    else if(errcode == BEC_LOCAL_TERMINATED){
    
    }
    else{
    
    }
       
}

void bt_connect_timer_cb(TimerHandle_t pxTimer)
{
    printf("bt connect timer cb\r\n");
    if(user_bt_env.connect_times > 0){
        user_bt_env.connect_times --;
        bt_connect(&user_bt_env.last_dev_addr, user_bt_env.last_connect_profile);
    }
}

void bt_avrcp_connect_timer_cb(TimerHandle_t pxTimer)
{   
    BtStatus status;
    printf("bt avrcp connect timer cb\r\n");

    uint8_t dev_index = (uint32_t)pvTimerGetTimerID(bt_avrcp_connect_timer);
    if(((user_bt_env.dev[dev_index].conFlags&LINK_STATUS_AVC_CONNECTED) == 0)
      && ((user_bt_env.dev[dev_index].conFlags&LINK_STATUS_AV_CONNECTED) == LINK_STATUS_AV_CONNECTED)){
        uint8_t index = bt_get_free_avrcp_channel();
        status = AVRCP_Connect(&rcpCtChannel[index],&user_bt_env.dev[dev_index].remote_bd);
        printf("avrcp connect status = %d\r\n",status);
    }
}

void bt_a2dp_connect_timer_cb(TimerHandle_t pxTimer)
{   
    BtStatus status;
    printf("bt a2dp connect timer cb\r\n");

    uint8_t dev_index = (uint32_t)pvTimerGetTimerID(bt_a2dp_connect_timer);
    if(((user_bt_env.dev[dev_index].conFlags&LINK_STATUS_AV_CONNECTED) == 0)
      && ((user_bt_env.dev[dev_index].conFlags&LINK_STATUS_HFG_CONNECTED) == LINK_STATUS_HFG_CONNECTED)){
        uint8_t index = bt_get_free_a2dp_source_stream();
        if(index < NUM_STREAMS){
            A2DP_OpenStream(&Stream[index], &user_bt_env.dev[dev_index].remote_bd);    
        }    
        printf("a2dp connect status = %d\r\n",status);
    }
}

void bt_linkloss_connect_timer_cb(TimerHandle_t pxTimer)
{
    printf("bt linkloss timer cb\r\n");
    uint8_t profiles = (uint32_t)pvTimerGetTimerID(bt_linkloss_connect_timer);
    bt_connect(&user_bt_env.last_dev_addr, profiles);
}

void bt_connect_cb(uint8_t type, BD_ADDR *addr, uint8_t errcode)
{
//    printf("bt connect cb:type = %d,addr=0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,err=%d\r\n",\
//            type,addr->A[0],addr->A[1],addr->A[2],addr->A[3],addr->A[4],addr->A[5],errcode);

    if((type == BT_EVENT_CON_CNF) && (errcode == BEC_PAGE_TIMEOUT) && (user_bt_env.connect_times > 0)){

        memcpy(&user_bt_env.last_dev_addr,addr,BD_ADDR_SIZE);
        xTimerStart(bt_connect_timer,portMAX_DELAY);
    }
}

static void bt_free_elt(void)
{
    struct bt_connect_elt_t *elt;
    GLOBAL_INT_DISABLE();
    elt = (struct bt_connect_elt_t *)co_list_pop_front(&user_bt_env.op_list);
    if(elt){
        user_bt_env.cur_action = BT_ACTION_NULL;
        user_bt_env.action_cnt --;
        btdm_free((void *)elt);
    }
    GLOBAL_INT_RESTORE();
}

uint8_t  bt_check_conn(uint8_t dev_index)
{
    uint16_t conn_flag = 0;
    uint16_t conn_flag_tmp;
    uint8_t ret = false;
    if(user_bt_env.enable_profiles & ENABLE_PROFILE_HF){
        conn_flag |= LINK_STATUS_HF_CONNECTED;
    }
    if(user_bt_env.enable_profiles & (ENABLE_PROFILE_A2DP_SINK | ENABLE_PROFILE_A2DP_SOURCE)){
        conn_flag |= LINK_STATUS_AV_CONNECTED | LINK_STATUS_AVC_CONNECTED;
    }
    if(user_bt_env.enable_profiles & ENABLE_PROFILE_HFG){
        conn_flag |= LINK_STATUS_HFG_CONNECTED;
    }
    
    if((conn_flag & (LINK_STATUS_HF_CONNECTED|LINK_STATUS_HFG_CONNECTED)) == (LINK_STATUS_HF_CONNECTED|LINK_STATUS_HFG_CONNECTED)){
        ///both supported,only check one profile
        conn_flag_tmp = conn_flag & (~LINK_STATUS_HF_CONNECTED);
        if((user_bt_env.dev[dev_index].conFlags & conn_flag_tmp) == conn_flag_tmp){
            ret = true;
        }
        conn_flag_tmp = conn_flag & (~LINK_STATUS_HFG_CONNECTED);
        if((user_bt_env.dev[dev_index].conFlags & conn_flag_tmp) == conn_flag_tmp){
            ret = true;
        }
    }
    else if((user_bt_env.dev[dev_index].conFlags & conn_flag) == conn_flag){
        ret = true;
    }
    printf("bt check conn flag = %x,profile=%x,conn_flg=%x,ret=%d\r\n",user_bt_env.dev[dev_index].conFlags,user_bt_env.enable_profiles,conn_flag,ret);

    return ret;
}

void bt_update_conn_status(uint8_t event, void *chan, const void *param)
{
    uint8_t dev_index;
    BD_ADDR addr;
    BtRemoteDevice *rm_dev = NULL;
    BtStatus status;
    //HfCallbackParms *info;
    
    
    if(event < BT_PROFILE_HF_MAX){
//        info = (HfCallbackParms *)param;
        if((event == BT_PROFILE_HF_CONN_REQ) || (event == BT_PROFILE_HF_CONN)){
            rm_dev = ((HfCallbackParms *)param)->p.remDev;
            dev_index = bt_find_dev_index_by_dev(rm_dev);
        }
        else{
            dev_index = bt_find_dev_index_by_hfchan(chan);
        } 
    }
    else if(event < BT_PROFILE_A2DP_MAX){
        if(event == BT_PROFILE_A2DP_DISCONN){
            A2dpCallbackParms *pm = (A2dpCallbackParms *)param;
            rm_dev = pm->p.device;
            if(rm_dev == NULL){
                rm_dev = A2DP_GetRemoteDevice((A2dpStream *)chan);
            }
            dev_index = bt_find_dev_index_by_dev(rm_dev);
        }
        else{
            rm_dev = A2DP_GetRemoteDevice((A2dpStream *)chan);
            dev_index = bt_find_dev_index_by_dev(rm_dev);
        }
    }
    else if(event < BT_PROFILE_AVRCP_MAX){
        if((event == BT_PROFILE_AVRCP_CONN) || (event == BT_PROFILE_AVRCP_DISCONN)){
            rm_dev = ((AvrcpCallbackParms *)param)->p.remDev;
            dev_index = bt_find_dev_index_by_dev(rm_dev);
        }
        else{
            dev_index = bt_find_dev_index_by_rcpchan(chan);
        } 
    }
    else if(event < BT_PROFILE_PBAP_MAX){
        dev_index = bt_find_dev_index_by_pbapclient(((PbapClientCallbackParms *)param)->client);
    }
    else if(event < BT_PROFILE_HFG_MAX){
        if(event == BT_PROFILE_HFG_AUDIO_DATA){
            dev_index = bt_find_dev_index_by_hfgchan(chan);
        }
        else{
        rm_dev = ((HfgCallbackParms *)param)->p.remDev;
        dev_index = bt_find_dev_index_by_dev(rm_dev);
    }
    }
    else if(event < BT_INFO_MAX){
        dev_index = 0;
    }
    if(dev_index >= NUM_BT_DEVICES){
        printf("!!!!error dev index, event = %d,%x,%x\r\n",event,(unsigned int)rm_dev,(unsigned int)user_bt_env.dev[0].remDev);
        return;
    }
    //printf("bt update conn %d\r\n",event);
    switch(event){
        case BT_PROFILE_HF_CONN_REQ:
            user_bt_env.dev[dev_index].responder = TRUE;
            user_bt_env.dev[dev_index].hf_chan = chan;
        break;
        
        case BT_PROFILE_HF_CONN:
        {
            user_bt_env.dev[dev_index].hf_chan = chan;
            user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_HF_CONNECTED;
            
            ///check connection
            if(true == bt_check_conn(dev_index)){
                bt_connect_act_cmp(BT_EVENT_PROFILE_CONNECT,0,rm_dev);
            }
            
            ///enable voltage notify
            bt_send_hf_cmd(dev_index,hf_enable_voltage_notify);
            
            ///search remote device type
            bt_send_hf_cmd(dev_index,hf_search_remote_dev_type);
            
            ///connect a2dp in master role
            if((user_bt_env.dev[dev_index].responder == FALSE)
                && ((user_bt_env.dev[dev_index].conFlags & LINK_STATUS_AV_CONNECTED) == 0)
                && (user_bt_env.enable_profiles & ENABLE_PROFILE_A2DP_SINK))
            {
                    uint8_t index = bt_get_free_a2dp_sink_stream();
                    status = A2DP_OpenStream(&Stream[index],&user_bt_env.dev[dev_index].remote_bd);
                    printf("a2dp open stream status=%d\r\n",status);
            }

        }
        break;
    
        case BT_PROFILE_HF_DISCONN:
        {
//            ME_GetBdAddr(info->p.remDev, addr);
//            dev_index = bt_find_dev_index(addr);
//            HfCallbackParms *info = (HfCallbackParms *)param;
//            dev_index = bt_find_dev_index_by_dev(info->p.remDev);
            user_bt_env.dev[dev_index].hf_chan = NULL;
            user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_HF_CONNECTED;
        }
        break;
        case BT_PROFILE_HF_CALL:
        {
            user_bt_env.dev[dev_index].active = ((HfCallbackParms *)param)->p.call;
        }
        break;
        case BT_PROFILE_HF_CALLSETUP:
        {
            user_bt_env.dev[dev_index].setup_state =((HfCallbackParms *)param)->p.callSetup;
        }
        break;
        case BT_PROFILE_HF_AUDIO_CONN:
        {
            system_prevent_sleep_set(SYSTEM_PREVENT_SLEEP_SCO_ONGOING);
            user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_SCO_CONNECTED;
            #ifdef ENABLE_AUDIO_TRANSFER
            /* connecting phone and earpods case*/
            if((NUM_BT_DEVICES == 2)&&(user_bt_env.dev[!dev_index].conFlags & LINK_STATUS_HFG_CONNECTED)){
                HFG_CreateAudioLink(user_bt_env.dev[!dev_index].hfg_chan, HFG_AR_LOCAL_USER_ACTION);
            }
            #endif
        }
        break;
        case BT_PROFILE_HF_AUDIO_DISCONN:
        {
            system_prevent_sleep_clear(SYSTEM_PREVENT_SLEEP_SCO_ONGOING);
            user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_SCO_CONNECTED;
            printf("hfg conflags = %x\r\n",user_bt_env.dev[!dev_index].conFlags);
            #ifdef ENABLE_AUDIO_TRANSFER
            /* connecting phone and earpods case*/
            if((NUM_BT_DEVICES == 2) && (user_bt_env.dev[!dev_index].conFlags & LINK_STATUS_SCO_CONNECTED)){
                HFG_DisconnectAudioLink(user_bt_env.dev[!dev_index].hfg_chan, HFG_AR_LOCAL_USER_ACTION);
            }     
            #endif
        }
        break;
        case BT_PROFILE_HF_AT_RESULT:
        {
            HfAtData *atdata = (HfAtData *)(((HfCallbackParms *)param)->p.data);
            printf("at result:");
            for(uint8_t i = 0; i < atdata->dataLen; i++)
            {
                printf("%c",atdata->data[i]);
            }            
            printf("\r\n");
        }
        break;
        case BT_PROFILE_HF_AUDIO_DATA:
        {
            #ifdef ENABLE_AUDIO_TRANSFER
            memcpy(voice_phone_data,((HfCallbackParms*)param)->p.audioData->data,((HfCallbackParms*)param)->p.audioData->len);
            
            BtPacket *sco_packet;
            BtStatus status;
            sco_packet = btdm_malloc(sizeof(BtPacket));
            memset((void *)sco_packet, 0, sizeof(BtPacket));
            sco_packet->data = btdm_malloc(120);
            memcpy(sco_packet->data, voice_headset_data, ((HfCallbackParms*)param)->p.audioData->len);
            sco_packet->dataLen = ((HfCallbackParms*)param)->p.audioData->len;
            status = HF_SendAudioData((HfChannel *)chan, sco_packet);
            if(status != BT_STATUS_PENDING){
                printf("no pending:%d\r\n",status);
                btdm_free((void *)sco_packet->data);
                btdm_free((void *)sco_packet);
            } 
            #endif
        }
        break;
        case BT_PROFILE_HF_SPK_VOL:
        {
            printf("HF SPK VOLUME: %d\r\n",((HfCallbackParms *)param)->p.gain);
        }
        break;
        case BT_PROFILE_HF_CURRENT_CALL:
        {
            const char *number = ((HfCallbackParms *)param)->p.callListParms->number;
            printf("current call number: ");
            for(uint8_t i = 0; i < strlen(number); i++)
            {
                printf("%c ",number[i]);
            }
            printf("\r\n");
        }
        break;
        case BT_PROFILE_A2DP_OPEN_IND:
        {
            user_bt_env.dev[dev_index].responder = TRUE;
        }
        break;
        
        case BT_PROFILE_A2DP_CONN:
        {
            user_bt_env.dev[dev_index].pstream = chan;
            user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_AV_CONNECTED;
            if(true == bt_check_conn(dev_index)){
                bt_connect_act_cmp(BT_EVENT_PROFILE_CONNECT,0,rm_dev);
            }
            if(((user_bt_env.dev[dev_index].conFlags&LINK_STATUS_AVC_CONNECTED) == 0)
              && (user_bt_env.dev[dev_index].responder == FALSE)){
                vTimerSetTimerID(bt_avrcp_connect_timer,(void *)(uint32_t)dev_index);
                xTimerStart(bt_avrcp_connect_timer,portMAX_DELAY);
            }
              
            AvdtpConfigRequest *req = ((A2dpCallbackParms *)param)->p.configReq;
            bt_save_codec_info(dev_index, &req->codec.elements[0]);
            printf("a2dp connected.%d\r\n",dev_index);
            printf("a2dp conn: %d,%x,%x,%x,%x\r\n",req->codec.codecType,req->codec.elements[0],req->codec.elements[1],req->codec.elements[2],req->codec.elements[3]);
#ifdef ENABLE_AUDIO_TRANSFER
            printf("bt_source_sbc_pkt_num: %d\r\n",user_bt_env.bt_source_sbc_pkt_num);
            if(user_bt_env.dev[!dev_index].conFlags&LINK_STATUS_AV_CONNECTED){
                if(memcmp(&sbcinfo[dev_index].elem[0],&sbcinfo[!dev_index].elem[0],4) != 0){
                    if((user_bt_env.dev[dev_index].connect_profile&ENABLE_PROFILE_A2DP_SOURCE)
                        &&(user_bt_env.dev[!dev_index].connect_profile&ENABLE_PROFILE_A2DP_SINK)){
    //                    uint8_t                  cfgElements[4] = {0x21,0x15,0x02,0x35};
                        AvdtpCodec          cfg_codec;
                        cfg_codec.codecType = AVDTP_CODEC_TYPE_SBC;
                        cfg_codec.elemLen = 4;
                        cfg_codec.elements = &sbcinfo[!dev_index].elem[0];
                        A2DP_ReconfigStream(user_bt_env.dev[dev_index].pstream,&cfg_codec,0);
                            printf("recong:%x,%x,%x,%x\r\n",cfg_codec.elements[0],cfg_codec.elements[1],cfg_codec.elements[2],cfg_codec.elements[3]);
                        memcpy(&sbcinfo[dev_index], &sbcinfo[!dev_index], sizeof(struct sbc_info_t));
                    }
                    else if((user_bt_env.dev[dev_index].connect_profile&ENABLE_PROFILE_A2DP_SINK)
                        &&(user_bt_env.dev[!dev_index].connect_profile&ENABLE_PROFILE_A2DP_SOURCE)){
                        AvdtpCodec          cfg_codec;
                        cfg_codec.codecType = AVDTP_CODEC_TYPE_SBC;
                        cfg_codec.elemLen = 4;
                        cfg_codec.elements = &sbcinfo[dev_index].elem[0];
                        A2DP_ReconfigStream(user_bt_env.dev[!dev_index].pstream,&cfg_codec,0);
                        memcpy(&sbcinfo[!dev_index], &sbcinfo[dev_index], sizeof(struct sbc_info_t));
                    }                
                }
                else{
                    printf("same sbc codec config\r\n");
                }
            }
#endif            
//            if(((user_bt_env.dev[dev_index].conFlags&LINK_STATUS_AVC_CONNECTED) == 0)
//              && (user_bt_env.dev[dev_index].responder == FALSE)){
//                uint8_t index = bt_get_free_avrcp_channel();
//                AVRCP_Connect(&rcpCtChannel[index],&user_bt_env.dev[dev_index].remote_bd);
//            }
        }
        break;
        
        case BT_PROFILE_A2DP_DISCONN:
        {
            user_bt_env.dev[dev_index].pstream = NULL;
            user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_AV_CONNECTED;
            printf("a2dp disconnected.%d\r\n",dev_index);

            #ifdef ENABLE_AUDIO_TRANSFER
            if((user_bt_env.dev[!dev_index].conFlags & LINK_STATUS_MEDIA_PLAYING)){
                /* connecting phone and earpods case*/
                ME_SetLinkPolicy(user_bt_env.dev[!dev_index].remDev,BLP_MASTER_SLAVE_SWITCH|BLP_SNIFF_MODE);
                A2DP_SuspendStream(user_bt_env.dev[!dev_index].pstream);
            }
            #endif
            system_prevent_sleep_clear(SYSTEM_PREVENT_SLEEP_A2DP_ONGOING);
        }
        break;
        
        case BT_PROFILE_A2DP_PLAYING:
        {
            user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_MEDIA_PLAYING;
            system_prevent_sleep_set(SYSTEM_PREVENT_SLEEP_A2DP_ONGOING);
            #ifdef ENABLE_AUDIO_TRANSFER
            /* connecting phone and earpods case*/
            if((user_bt_env.dev[!dev_index].conFlags & LINK_STATUS_AV_CONNECTED)){
                ME_SetLinkPolicy(user_bt_env.dev[!dev_index].remDev,BLP_MASTER_SLAVE_SWITCH);//disable sniff
                A2DP_StartStream(user_bt_env.dev[!dev_index].pstream);
            }    
            #endif
        }
        break;
        
        case BT_PROFILE_A2DP_SUSPEND:
        {
            user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_MEDIA_PLAYING;
            system_prevent_sleep_clear(SYSTEM_PREVENT_SLEEP_A2DP_ONGOING);
            #ifdef ENABLE_AUDIO_TRANSFER
            /* connecting phone and earpods case*/
            if((user_bt_env.dev[!dev_index].conFlags & LINK_STATUS_MEDIA_PLAYING)){
                ME_SetLinkPolicy(user_bt_env.dev[!dev_index].remDev,BLP_MASTER_SLAVE_SWITCH|BLP_SNIFF_MODE);
                A2DP_SuspendStream(user_bt_env.dev[!dev_index].pstream);
            }
            #endif

        }
        break;
        case BT_PROFILE_A2DP_STREAM_DATA:
        {
            #ifdef ENABLE_AUDIO_TRANSFER
            if(user_bt_env.dev[!dev_index].conFlags & LINK_STATUS_MEDIA_PLAYING)
            {
                BtStatus status;
//                uint16_t temp = (1*sbcinfo[dev_index].info.numSubBands) + (sbcinfo[dev_index].info.numBlocks*sbcinfo[dev_index].info.bitPool);
//                uint16_t frame_len = 4 + ((4*sbcinfo[dev_index].info.numSubBands*sbcinfo[dev_index].info.numChannels)>>3) + (temp>>3);
//                if(temp%8){
//                    frame_len++;
//                }         
                uint16_t frame_len = sbc_calc_frame_len(&sbcinfo[dev_index]);
                
//                printf("send frame len=%d\r\n",frame_len);
                if(user_bt_env.bt_source_sbc_pkt_num < 10){
                    A2dpSbcPacket *packet = (A2dpSbcPacket *)btdm_malloc(sizeof(A2dpSbcPacket));
                    packet->frameSize = frame_len;
                    packet->dataLen = ((A2dpCallbackParms *)param)->len - 13;
                    packet->data = (uint8_t *)btdm_malloc(packet->dataLen);
                    memcpy(packet->data,((A2dpCallbackParms *)param)->p.data + 13,packet->dataLen);
                    status = A2DP_StreamSendSbcPacket(user_bt_env.dev[!dev_index].pstream,packet,&sbcinfo[!dev_index].info);
                    
                    if(status != BT_STATUS_PENDING){
                        printf("err sending sbc data\r\n");
                        btdm_free(packet->data);
                        btdm_free(packet);
                    }
                    else{
                        user_bt_env.bt_source_sbc_pkt_num ++;
                    }
                }
            }
            #endif
        }    
        break;
        case BT_PROFILE_A2DP_SBC_PKT_SENT:
            btdm_free(((A2dpCallbackParms *)param)->p.sbcPacket->data);
            btdm_free(((A2dpCallbackParms *)param)->p.sbcPacket);
            user_bt_env.bt_source_sbc_pkt_num--;
        break;
        case BT_PROFILE_AVRCP_CONN:
        {
            user_bt_env.dev[dev_index].rcp_chan = chan;
            user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_AVC_CONNECTED;
            if(true == bt_check_conn(dev_index)){
                bt_connect_act_cmp(BT_EVENT_PROFILE_CONNECT,0,rm_dev);
            }
        }
        break;
        
        case BT_PROFILE_AVRCP_DISCONN:
        {
            user_bt_env.dev[dev_index].rcp_chan = NULL;
            user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_AVC_CONNECTED;
        }
        break;
        
        case BT_PROFILE_AVRCP_EVENT_SUPPORT:
        {
            uint16_t *event_mask = (uint16_t *)param;
            uint16_t event_sup = (*event_mask) & (AVRCP_ENABLE_PLAY_STATUS_CHANGED|AVRCP_ENABLE_TRACK_CHANGED);
            if(user_bt_env.dev[dev_index].connect_profile&ENABLE_PROFILE_A2DP_SINK){
                bt_avrcp_register_notification(chan,event_sup);
            }
            else{
                bt_avrcp_register_notification(chan,AVRCP_ENABLE_VOLUME_CHANGED);
            }
        }
        break;
        
        case BT_PROFILE_AVRCP_MEDIA_STATUS:
        {
            uint8_t *media_status = (uint8_t *)param;
            if((*media_status == AVRCP_MEDIA_PAUSED) || (*media_status == AVRCP_MEDIA_STOPPED)){
                printf("media paused\r\n");
                user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_MEDIA_PLAYING;
            }
            else{
                printf("media playing\r\n");
                user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_MEDIA_PLAYING;
            }
        }
        break;
        
        case BT_PROFILE_PBAP_CONN:
        {
            user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_PBAP_CONNECTED;
        }    
        break;
        
        case BT_PROFILE_PBAP_DISCONN:
        {
            user_bt_env.dev[dev_index].pbap_client = NULL;
            user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_PBAP_CONNECTED;
        }    
        break;
        
        case BT_PROFILE_PBAP_DATA_IND:
        {
            printf("data ind: \r\n");
            for(uint16_t i = 0; i < ((PbapClientCallbackParms *)param)->u.dataInd.len; i++)
            {
                printf("%c",((PbapClientCallbackParms *)param)->u.dataInd.buffer[i]);
            }
            printf("\r\n");
        }    
        break;
        
        case BT_PROFILE_PBAP_COMP:
            
        break;
        case BT_PROFILE_HFG_CONN_REQ:
            user_bt_env.dev[dev_index].responder = TRUE;
        break;
        case BT_PROFILE_HFG_CONN:
            user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_HFG_CONNECTED;
            user_bt_env.dev[dev_index].hfg_chan = chan;
        
            ///check connection
            if(true == bt_check_conn(dev_index)){
                bt_connect_act_cmp(BT_EVENT_PROFILE_CONNECT,0,rm_dev);
            }
            
            if(((user_bt_env.dev[dev_index].conFlags & LINK_STATUS_AV_CONNECTED) == 0)
                && (user_bt_env.enable_profiles & ENABLE_PROFILE_A2DP_SOURCE)){
                if(user_bt_env.dev[dev_index].responder == FALSE){
                uint8_t index = bt_get_free_a2dp_source_stream();
                if(index < NUM_STREAMS){
                    A2DP_OpenStream(&Stream[index], &user_bt_env.dev[dev_index].remote_bd);    
                }
            }
                else{
                    printf("...start a2dp timer\r\n");
                    vTimerSetTimerID(bt_a2dp_connect_timer,(void *)(uint32_t)dev_index);
                    xTimerStart(bt_a2dp_connect_timer,portMAX_DELAY);
                }
            }
        break;
        case BT_PROFILE_HFG_DISCONN:
            user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_HFG_CONNECTED;
            user_bt_env.dev[dev_index].hfg_chan = NULL;
        break;
        
        case BT_PROFILE_HFG_AUDIO_CONN:
        {
            ///disable sniff
            ME_SetLinkPolicy(user_bt_env.dev[dev_index].remDev,BLP_MASTER_SLAVE_SWITCH);
            user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_SCO_CONNECTED;
        }
        break;
        
        case BT_PROFILE_HFG_AUDIO_DISCONN:
        {
            ///enable sniff
            ME_SetLinkPolicy(user_bt_env.dev[dev_index].remDev,BLP_MASTER_SLAVE_SWITCH|BLP_SNIFF_MODE);
            user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_SCO_CONNECTED;
        }
        break;
        case BT_PROFILE_HFG_AUDIO_DATA:
//                BtPacket *sco_packet;
//                sco_packet = btdm_malloc(sizeof(BtPacket));
//                memset((void *)sco_packet, 0, sizeof(BtPacket));
//                sco_packet->data = btdm_malloc(120);
//                memcpy(sco_packet->data, Info->p.audioData->data, Info->p.audioData->len);
//                sco_packet->dataLen = Info->p.audioData->len;
//                status = HF_SendAudioData(Chan, sco_packet);
//                if(status != BT_STATUS_PENDING){
//                    printf("no pending:%d\r\n",status);
//                    btdm_free((void *)sco_packet->data);
//                    btdm_free((void *)sco_packet);
//                }                
        
        {
            #ifdef ENABLE_AUDIO_TRANSFER
            BtPacket *sco_packet;
            sco_packet = btdm_malloc(sizeof(BtPacket));
            memset((void *)sco_packet, 0, sizeof(BtPacket));
            sco_packet->data = btdm_malloc(120);
            memcpy(sco_packet->data, voice_phone_data, ((HfCallbackParms*)param)->p.audioData->len);
            memcpy(voice_headset_data, ((HfCallbackParms*)param)->p.audioData->data, ((HfCallbackParms*)param)->p.audioData->len);
            ///loopback
            sco_packet->dataLen = ((HfCallbackParms*)param)->p.audioData->len;
            status = HFG_SendAudioData((HfgChannel *)chan, sco_packet);
            if(status != BT_STATUS_PENDING){
                printf("no pending:%d\r\n",status);
                btdm_free((void *)sco_packet->data);
                btdm_free((void *)sco_packet);
            } 
            #endif
        }        
        break;
        case BT_INFO_INQ_RESULT:
        {
            BtInquiryResult    *inqResult = (BtInquiryResult *)param;
            uint8_t name[MAX_NAME_SIZE];
            uint8_t len;
            printf("inq result: %x\r\n",inqResult->classOfDevice);
            printf("addr:0x%02x%02x%02x%02x%02x%02x\r\n",inqResult->bdAddr.A[0],inqResult->bdAddr.A[1],inqResult->bdAddr.A[2],
                    inqResult->bdAddr.A[3],inqResult->bdAddr.A[4],inqResult->bdAddr.A[5]);
            printf("rssi:%d,name: ",inqResult->rssi);
            bt_parse_inq_result((uint8_t *)inqResult->extInqResp,name,&len);   
            for(uint8_t i = 0; i < len; i++)
            {
                printf("%c",name[i]);
            }
            printf("\r\n");
            if(len == 0){
                bt_name_query(&inqResult->bdAddr, &inqResult->psi);  
            }
        }
        break;
        
        case BT_INFO_INQ_CANCEL:
        {
            printf("inquiry cancelled\r\n");
        }
        break;
        case BT_INFO_INQ_COMP:
        {
            //do inquiry again
            ME_Inquiry(BT_IAC_GIAC, 5, 5);
        }
        break;
    }
}

CmgrHandler* bt_get_cmgr_handler(uint8_t dev_index)
{
    CmgrHandler *handler = NULL;
    if(user_bt_env.dev[dev_index].hf_chan){
        handler = &user_bt_env.dev[dev_index].hf_chan->cmgrHandler;
    }
    else if(user_bt_env.dev[dev_index].hfg_chan){
        handler = &user_bt_env.dev[dev_index].hfg_chan->cmgrHandler;
    }
    else if(user_bt_env.dev[dev_index].pstream){
        handler = &user_bt_env.dev[dev_index].pstream->device->cmgrHandler;
    }
    
    return handler;
}

void bt_connect_act_cmp(uint8_t evt,uint8_t error,BtRemoteDevice *rem_dev)
{
    uint8_t dev_index;
    struct bt_connect_elt_t *elt;
    uint8_t cur_act = BT_ACTION_NULL;
    BD_ADDR *cur_addr = NULL;
    uint8_t check_op_list = false;    
    BtStatus ret;
    
    elt = (struct bt_connect_elt_t *)co_list_pick(&user_bt_env.op_list);
    if(elt){
        cur_act = elt->action;
        cur_addr = &elt->addr;
    }
    printf("bt_connect_act_cmp:evt=%d,act = %d,error=%d\r\n",evt,elt->action,error);
    switch(evt){
        case BT_EVENT_CON_IND:
        case BT_EVENT_CON_CNF:
            dev_index = bt_find_dev_index(&rem_dev->bdAddr);
            
            if(dev_index >= NUM_BT_DEVICES){
                if(evt == BT_EVENT_CON_IND){
                    dev_index = bt_get_free_dev();
                    assert(dev_index < NUM_BT_DEVICES);
                }
                else{
                    printf("shall not be here...,%s,%d\r\n",__FUNCTION__,__LINE__);
                }
            }
            printf("rem dev cod:%x\r\n",rem_dev->cod);
            if(evt == BT_EVENT_CON_IND){
                if((rem_dev->cod & 0x0400) == 0x0400){
                    user_bt_env.dev[dev_index].connect_profile = ENABLE_PROFILE_HFG|ENABLE_PROFILE_A2DP_SOURCE;
                }
                else{
                    user_bt_env.dev[dev_index].connect_profile = ENABLE_PROFILE_HF|ENABLE_PROFILE_A2DP_SINK;
                }
            }
            if(user_bt_env.bt_connect_cb){
                user_bt_env.bt_connect_cb(evt,&rem_dev->bdAddr, error);
            }
            if(error == BEC_NO_ERROR){

                user_bt_env.dev[dev_index].remDev = rem_dev;
                user_bt_env.dev[dev_index].state = BT_LINK_STATE_ACL_CONNECTED;
                user_bt_env.last_active_index = dev_index;
                memcpy(&user_bt_env.dev[dev_index].remote_bd,&rem_dev->bdAddr,BD_ADDR_SIZE);
                if((memcmp(&rem_dev->bdAddr,cur_addr,BD_ADDR_SIZE) == 0)&&(cur_act == BT_ACTION_DISCONNECT)){
                    bt_do_disconnect(cur_addr,bt_get_cmgr_handler(dev_index),true);
                }
            }
            else{
                user_bt_env.dev[dev_index].state = BT_LINK_STATE_IDLE;
                memset(&user_bt_env.dev[dev_index].remote_bd,0,BD_ADDR_SIZE);
                if((cur_act == BT_ACTION_CONNECT) || (cur_act == BT_ACTION_DISCONNECT)){
                    bt_free_elt();
                    check_op_list = true;
                }
            }   

        break;           
            
        case BT_EVENT_PROFILE_CONNECT:
            dev_index = bt_find_dev_index(&rem_dev->bdAddr);
            user_bt_env.dev[dev_index].state = BT_LINK_STATE_PROFILE_CONNECTED;
            memcpy(&user_bt_env.last_dev_addr,&rem_dev->bdAddr,BD_ADDR_SIZE);

            printf("\r\nIV\r\n");
            if(user_bt_env.bt_connect_cb){
                user_bt_env.bt_connect_cb(evt,&rem_dev->bdAddr, error);
            }
            if(cur_act == BT_ACTION_CONNECT){
                bt_free_elt();
                check_op_list = true;
            }
        break;

        case BT_EVENT_DISCONNECT:
            dev_index = bt_find_dev_index(&rem_dev->bdAddr);
            user_bt_env.dev[dev_index].state = BT_LINK_STATE_IDLE;
            if(user_bt_env.bt_disconnect_cb){
                user_bt_env.bt_disconnect_cb(&rem_dev->bdAddr, error);
            }
            
            if((cur_act == BT_ACTION_DISCONNECT) || (cur_act == BT_ACTION_CONNECT)){
                bt_free_elt();
                check_op_list = true;
            }
            memset(&user_bt_env.dev[dev_index],0,sizeof(APP_DEVICE));
        break;
            
        case BT_EVENT_ACC_CHG:
            if(user_bt_env.bt_access_change_cb){
                user_bt_env.bt_access_change_cb(error);
            }
            user_bt_env.access_state = error;
            if(cur_act == BT_ACTION_ACCESS){
                bt_free_elt();
                check_op_list = true;
            }
        break;
    }

    ///check pending op list
    if(check_op_list == true){
        elt = (struct bt_connect_elt_t *)co_list_pick(&user_bt_env.op_list);
        while(elt && (check_op_list == true))
        {
            printf("check pending list act=%d,state=%d,addr0=%x\r\n",elt->action,user_bt_env.dev[0].state,user_bt_env.dev[0].remote_bd.A[0]);
            switch(elt->action){
                case BT_ACTION_CONNECT:
                {
                    dev_index = bt_find_dev_index(&elt->addr);
                    if(dev_index >= NUM_BT_DEVICES){
                        //device not exist, find free dev
                        dev_index = bt_get_free_dev();
                        if(dev_index < NUM_BT_DEVICES){
                            //get free dev, create new connection
                            void *chan;
                            ret = bt_do_connect(&elt->addr, elt->profiles, &chan);
                            if(ret == BT_STATUS_PENDING){
                                user_bt_env.cur_action = BT_ACTION_CONNECT;
                                user_bt_env.dev[dev_index].connect_profile = elt->profiles;
                                user_bt_env.last_connect_profile = elt->profiles; 
                                user_bt_env.dev[dev_index].state = BT_LINK_STATE_CONNECTING;
                                memcpy(&user_bt_env.dev[dev_index].remote_bd,&elt->addr,sizeof(BD_ADDR));
                                if(elt->profiles & ENABLE_PROFILE_HF){
                                    user_bt_env.dev[dev_index].hf_chan = chan;
                                }
                                check_op_list = false;
                            }
                            else{
                                bt_free_elt();
                            }
                        }
                        else{
                            //no free dev
                            bt_free_elt();
                        }
                    }
                    else{
                        //device exist,check device state
                        if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_PROFILE_CONNECTED){
                            //profile connected,return success
                            bt_free_elt();
                        }else{
                            check_op_list = false;
                        }
                    }
                }
                break;
                case BT_ACTION_DISCONNECT:
                {
                    dev_index = bt_find_dev_index(&elt->addr);
                    if(dev_index >= NUM_BT_DEVICES){
                        //don't find device
                        bt_free_elt();
                    }
                    else{
                        if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_PROFILE_CONNECTED){
                            //profile connected,return success
                            user_bt_env.dev[dev_index].state = BT_LINK_STATE_DISCONNECTING;
                            user_bt_env.cur_action = BT_ACTION_DISCONNECT;
                            bt_do_disconnect(&elt->addr,NULL,false);
                        }
                        //other state,just return
                        check_op_list = false;
                    }
                }
                break;
                case BT_ACTION_ACCESS:
                {
                    printf("shall not be here..error action access change.");
                    bt_free_elt();
                }
                break;
            }
            elt = (struct bt_connect_elt_t *)co_list_pick(&user_bt_env.op_list);
        }
    }    
}


void bt_access_change_cb(uint8_t mode)
{
    printf("access change cb: %d\r\n",mode);
}

BtStatus bt_connect(BD_ADDR *addr, uint8_t profiles)
{
    BtStatus ret = BT_STATUS_PENDING;
    uint8_t dev_index = 0;
    struct bt_connect_elt_t *elt;
    uint8_t add_to_list = false;
    
    printf("bt connect: %x,%x,%x\r\n",addr->A[0],addr->A[1],profiles);

    //param check
    assert(addr != NULL);
    
//    //error bt state, return failed
//    for(index = 0; index < NUM_BT_DEVICES; index++){
//        if((user_bt_env.dev[index].state == BT_LINK_STATE_CONNECTING) || (user_bt_env.dev[index].state == BT_LINK_STATE_DISCONNECTING)){
//            return BT_STATUS_FAILED;
//        }
//    }

    elt = (struct bt_connect_elt_t *)btdm_malloc(sizeof(struct bt_connect_elt_t));
    memcpy(&elt->addr,addr,BD_ADDR_SIZE);
    elt->action = BT_ACTION_CONNECT;
    if(co_list_is_empty(&user_bt_env.op_list)){
        //list empty, check device index
        dev_index = bt_find_dev_index(addr);
        if(dev_index >= NUM_BT_DEVICES){
            //not connection with the addr
            dev_index = bt_get_free_dev();
            if(dev_index >= NUM_BT_DEVICES){
                //no free device
                printf("err...dev state %d\r\n",user_bt_env.dev[0].state);
                btdm_free(elt);
                return BT_STATUS_NO_RESOURCES;
            }
        }
        printf("connect state=%d\r\n",user_bt_env.dev[dev_index].state);
        if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_IDLE){
            //idle state, do connect imediately
             void *chan;
            ret = bt_do_connect(addr,profiles,&chan);
            if(ret == BT_STATUS_PENDING){
                add_to_list = true;
                user_bt_env.cur_action = BT_ACTION_CONNECT;
                user_bt_env.dev[dev_index].state = BT_LINK_STATE_CONNECTING;
                user_bt_env.dev[dev_index].connect_profile = profiles;
                user_bt_env.last_connect_profile = profiles; 
                memcpy(&user_bt_env.dev[dev_index].remote_bd,addr,sizeof(BD_ADDR));
                if(profiles & ENABLE_PROFILE_HF){
                    user_bt_env.dev[dev_index].hf_chan = chan;
                }
                else if(profiles & ENABLE_PROFILE_HFG){
                    user_bt_env.dev[dev_index].hfg_chan = chan;
                }
                else if(profiles & (ENABLE_PROFILE_A2DP_SINK | ENABLE_PROFILE_A2DP_SOURCE)){
                    user_bt_env.dev[dev_index].pstream = chan;
                }                  
            }
            else{
                btdm_free(elt);
            }
        }
        else if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_DISCONNECTING){
            //disconnecting,usally shall not be here
            add_to_list = true;
        }
        else if((user_bt_env.dev[dev_index].state == BT_LINK_STATE_CONNECTING)
            || (user_bt_env.dev[dev_index].state == BT_LINK_STATE_ACL_CONNECTED)){
            //connecting or acl connected,just free connect elt,return pending
            btdm_free(elt);
        }
        else if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_PROFILE_CONNECTED){
            //profile connected,return success
            btdm_free(elt);
            ret = BT_STATUS_SUCCESS;
        }
    }else{
        add_to_list = true;
    }
    
    if(add_to_list == true){
        elt->profiles = profiles;
        GLOBAL_INT_DISABLE();
        co_list_push_back(&user_bt_env.op_list,&elt->hdr);
        user_bt_env.action_cnt ++;
        GLOBAL_INT_RESTORE();
    }
    
    return ret;
}

BtStatus bt_disconnect(BD_ADDR *addr, uint8_t force_disconnect) 
{
    BtStatus status = BT_STATUS_PENDING;
    uint8_t dev_index = 0;
    struct bt_connect_elt_t *elt,*cur_elt;
    uint8_t add_to_list = false;
    printf("bt disconnect\r\n");
    if(addr == NULL){
        return BT_STATUS_FAILED;
    }
    elt = (struct bt_connect_elt_t *)btdm_malloc(sizeof(struct bt_connect_elt_t));
    memcpy(&elt->addr,addr,BD_ADDR_SIZE);
    elt->action = BT_ACTION_DISCONNECT;
    
    if((co_list_is_empty(&user_bt_env.op_list)) || (force_disconnect == true)){
        dev_index = bt_find_dev_index(addr);
        printf("cur state=%d\r\n",user_bt_env.dev[dev_index].state);
        if(dev_index < NUM_BT_DEVICES){
            if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_IDLE){
                memset(&user_bt_env.dev[dev_index].remote_bd,0,sizeof(BD_ADDR));
                status = BT_STATUS_SUCCESS;
            }
            else if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_DISCONNECTING){
                add_to_list = false;
                if(force_disconnect == true){
//                    cur_elt = (struct bt_connect_elt_t *)co_list_pick(&user_bt_env.op_list);
                    bt_do_disconnect(addr,bt_get_cmgr_handler(dev_index),force_disconnect);
                }
            }
            else if((user_bt_env.dev[dev_index].state == BT_LINK_STATE_CONNECTING)
                || (user_bt_env.dev[dev_index].state == BT_LINK_STATE_ACL_CONNECTED))
            {
                add_to_list = true;
                if(force_disconnect == true){
                    GLOBAL_INT_DISABLE();
                    cur_elt = (struct bt_connect_elt_t *)co_list_pick(&user_bt_env.op_list);
                    if(cur_elt && (cur_elt->action == BT_ACTION_CONNECT)){
                        co_list_pop_front(&user_bt_env.op_list);
                        user_bt_env.action_cnt --;
                        btdm_free(cur_elt);
                    }
                    GLOBAL_INT_RESTORE();
                        user_bt_env.dev[dev_index].state = BT_LINK_STATE_DISCONNECTING;
                    status = bt_do_disconnect(addr,bt_get_cmgr_handler(dev_index),force_disconnect);
                    if(status != BT_STATUS_PENDING){
                        add_to_list = false;
                        user_bt_env.dev[dev_index].state = BT_LINK_STATE_IDLE;
                    }

                }
            }
            else if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_PROFILE_CONNECTED){
                user_bt_env.dev[dev_index].state = BT_LINK_STATE_DISCONNECTING;
                bt_do_disconnect(addr,NULL,force_disconnect);
                add_to_list = true;
            }
        }
        else{
            btdm_free(elt);
            return BT_STATUS_FAILED;
        }
    }
    else{
        add_to_list = true;
    }
    
    if(add_to_list == true){
        printf("add to list\r\n");
        GLOBAL_INT_DISABLE();
        co_list_push_back(&user_bt_env.op_list,&elt->hdr);
        user_bt_env.action_cnt ++;
        GLOBAL_INT_RESTORE();
    }
    else{
        btdm_free(elt);
    }
    
    return status;
}

void user_bt_init(void)
{
    struct bt_connect_elt_t *elt;

    while(!co_list_is_empty(&user_bt_env.op_list)){
        user_bt_env.action_cnt --;
        elt = (struct bt_connect_elt_t *)co_list_pop_front(&user_bt_env.op_list);
        btdm_free((void *)elt);
    }
    memset((void *)&user_bt_env,0,sizeof(struct user_bt_env_t));
    co_list_init(&user_bt_env.op_list);
    user_bt_env.bt_disconnect_cb = bt_disconnect_cb;
    user_bt_env.bt_connect_cb = bt_connect_cb;
    user_bt_env.bt_access_change_cb = bt_access_change_cb;
    user_bt_env.enable_profiles = ENABLE_PROFILE_HFG | ENABLE_PROFILE_A2DP_SINK | ENABLE_PROFILE_HF | ENABLE_PROFILE_A2DP_SOURCE ;
    user_bt_env.enable_local_ring = false;
    user_bt_env.connect_times = MAX_RECONNECT_TIMES;
    user_bt_env.page_timeout = PAGE_TIMEOUT;
    ME_SetPageTimeout(user_bt_env.page_timeout);
    
#ifdef ENABLE_AUDIO_TRANSFER
    CMGR_SetAudioVoiceSettings(0x60);
    #if BTDM_STACK_ENABLE_AG
    ///only support cvsd
    HFG_SetSupportedFeature(HFG_SDK_FEATURES&~HFG_FEATURE_CODEC_NEGOTIATON);
    #endif
    #if BTDM_STACK_ENABLE_HF
    HF_SetSupportedFeature(HF_SDK_FEATURES&~HF_FEATURE_CODEC_NEGOTIATION_CFG);
    #endif
#endif
    
    for (uint8_t i=0; i<NUM_BT_DEVICES; i++) {
        sbcinfo[i].info.allocMethod = 1; //1--loudness, 2-- snr
        sbcinfo[i].info.sampleFreq = 2; // 0--16k, 1--32k, 2--44.1k, 3--48k
        sbcinfo[i].info.bitPool = 0x35;
        sbcinfo[i].info.channelMode = 3; // 0--mono, 1-- dual_chal, 2--stereo, 3--joint stereo
        sbcinfo[i].info.numBlocks = 16;
        sbcinfo[i].info.numChannels = 2;
        sbcinfo[i].info.numSubBands = 8;
        sbcinfo[i].elem[0] = 0x21;
        sbcinfo[i].elem[1] = 0x15;
        sbcinfo[i].elem[2] = 0x02;
        sbcinfo[i].elem[3] = 0x35;

    }     
    if(bt_connect_timer == NULL){
        bt_connect_timer = xTimerCreate("bt_connect_timer", 10, pdFALSE, 0, bt_connect_timer_cb );
    }

    if(bt_avrcp_connect_timer == NULL){
        bt_avrcp_connect_timer = xTimerCreate("bt_avrcp_connect_timer", 500, pdFALSE, 0, bt_avrcp_connect_timer_cb );
    }
    
    if(bt_a2dp_connect_timer == NULL){
        bt_a2dp_connect_timer = xTimerCreate("bt_a2dp_connect_timer", 4000, pdFALSE, 0, bt_a2dp_connect_timer_cb );
    }
        
    if(bt_linkloss_connect_timer == NULL){
        bt_linkloss_connect_timer = xTimerCreate("bt_linkloss_connect_timer", 500, pdFALSE, 0, bt_linkloss_connect_timer_cb );
    }
}

uint8_t user_bt_get_state(uint8_t dev_index)
{
    uint8_t state = BT_STATE_IDLE;
    
    switch(user_bt_env.dev[dev_index].state)
    {
        case BT_LINK_STATE_IDLE:
            state = BT_STATE_IDLE;
            break;
        
        case BT_LINK_STATE_CONNECTING:
            state = BT_STATE_CONNECTING;
            break;
        
        case BT_LINK_STATE_DISCONNECTING:
            state = BT_STATE_DISCONNECTING;
            break;
        
        case BT_LINK_STATE_ACL_CONNECTED:
        case BT_LINK_STATE_PROFILE_CONNECTED:
        {
            state = BT_STATE_CONNECTED;
            if(user_bt_env.dev[dev_index].active == HF_CALL_ACTIVE){
                state = BT_STATE_HFP_CALLACTIVE;
            }
            else if(user_bt_env.dev[dev_index].setup_state == HF_CALL_SETUP_IN){
                state = BT_STATE_HFP_INCOMMING;
            }
            else if((user_bt_env.dev[dev_index].setup_state == HF_CALL_SETUP_OUT)
                ||(user_bt_env.dev[dev_index].setup_state == HF_CALL_SETUP_ALERT)){
                state = BT_STATE_HFP_OUTGOING;
            }else if(user_bt_env.dev[dev_index].conFlags & LINK_STATUS_MEDIA_PLAYING){
                state = BT_STATE_MEDIA_PLAYING;
            }    
        }
        break;        
    }
    
    return state;
}

BtStatus bt_enter_pairing(uint8_t access, BtAccessModeInfo *info)
{
    BtStatus status = BT_STATUS_PENDING;
    struct bt_connect_elt_t *elt;
    printf("bt enter pairing\r\n");
   
    
    
    if((access == BAM_NOT_ACCESSIBLE) || (access > BAM_GENERAL_ACCESSIBLE)){
        return BT_STATUS_INVALID_PARM;
    }
    if(user_bt_env.access_state == access){
        return BT_STATUS_SUCCESS;
    }
    
    //TBD,only change nonconnected access state
    status = ME_SetAccessibleModeNC(access, info);
    if(status == BT_STATUS_PENDING){
        elt = (struct bt_connect_elt_t *)btdm_malloc(sizeof(struct bt_connect_elt_t));
        elt->action = BT_ACTION_ACCESS;
        GLOBAL_INT_DISABLE();
        co_list_push_back(&user_bt_env.op_list,&elt->hdr);
        user_bt_env.action_cnt ++;
        GLOBAL_INT_RESTORE();
    }
    
    return status;
}

BtStatus bt_exit_pairing(void)
{
    BtStatus status = BT_STATUS_PENDING;
    struct bt_connect_elt_t *elt;

    status = ME_SetAccessibleModeNC(BAM_NOT_ACCESSIBLE, NULL);
    if(status == BT_STATUS_PENDING){
        elt = (struct bt_connect_elt_t *)btdm_malloc(sizeof(struct bt_connect_elt_t));
        elt->action = BT_ACTION_ACCESS;
        GLOBAL_INT_DISABLE();
        co_list_push_back(&user_bt_env.op_list,&elt->hdr);
        user_bt_env.action_cnt ++;
        GLOBAL_INT_RESTORE();
    }

    return status;
}

BtStatus bt_answer_call(uint8_t dev_index)
{
    HfCommand *cmd;
    uint8_t state = user_bt_get_state(dev_index);
    
    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }
    
    if(state != BT_STATE_HFP_INCOMMING){
        return BT_STATUS_FAILED;
    }
    cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
    return HF_AnswerCall(user_bt_env.dev[dev_index].hf_chan, cmd);
}

BtStatus bt_dial_number(uint8_t dev_index, uint8_t *number, uint16_t len)
{

    HfCommand *cmd;
    
    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }

    cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
    return HF_DialNumber(user_bt_env.dev[dev_index].hf_chan, number, len, cmd);
}

BtStatus bt_redial(uint8_t dev_index)
{
    HfCommand *cmd;
    
    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }

    cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
    return HF_Redial(user_bt_env.dev[dev_index].hf_chan, cmd);
}

BtStatus bt_call_hold(uint8_t dev_index, HfHoldAction action, uint8_t index)
{
    HfCommand *cmd;
    
    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }

    cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
    return HF_CallHold(user_bt_env.dev[dev_index].hf_chan, action, index, cmd);
}
                     
BtStatus bt_hang_up(uint8_t dev_index)
{
    HfCommand *cmd;
    uint8_t state = user_bt_get_state(dev_index);

    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }
    printf("state = %d\r\n",state);
    if((state != BT_STATE_HFP_INCOMMING) && (state != BT_STATE_HFP_OUTGOING) && (state != BT_STATE_HFP_CALLACTIVE)){
        return BT_STATUS_FAILED;
    }
    cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
    return HF_Hangup(user_bt_env.dev[dev_index].hf_chan, cmd);
}

BtStatus bt_list_current_calls(uint8_t dev_index)
{
    HfCommand *cmd;
    uint8_t state = user_bt_get_state(dev_index);

    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }
    printf("state = %d\r\n",state);

    if((state != BT_STATE_HFP_INCOMMING) && (state != BT_STATE_HFP_OUTGOING) && (state != BT_STATE_HFP_CALLACTIVE)){
        return BT_STATUS_FAILED;
    }
    cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
    return HF_ListCurrentCalls(user_bt_env.dev[dev_index].hf_chan, cmd);
}

BtStatus bt_transfer_sco(uint8_t dev_index)
{
    HfCommand *cmd;
    uint8_t state = user_bt_get_state(dev_index);

    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }
    if(state < BT_STATE_CONNECTED){
        return BT_STATUS_FAILED;
    }
    printf("conFlags = %d\r\n",user_bt_env.dev[dev_index].conFlags);
    if(user_bt_env.dev[dev_index].conFlags & LINK_STATUS_SCO_CONNECTED){
        return HF_DisconnectAudioLink(user_bt_env.dev[dev_index].hf_chan);
    }else{
        cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
        //return HF_CreateAudioLink(user_bt_env.dev[dev_index].hf_chan);
        return HF_CodecConnectionReq(user_bt_env.dev[dev_index].hf_chan, cmd);
    }
}

BtStatus bt_send_dtmf(uint8_t dev_index, uint8_t dtmf)
{
    HfCommand *cmd;

    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }

    cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
    return HF_GenerateDtmf(user_bt_env.dev[dev_index].hf_chan, dtmf, cmd);
}

BtStatus bt_report_mic_volume(uint8_t dev_index, uint8_t vol)
{
    HfCommand *cmd;
    uint8_t state = user_bt_get_state(dev_index);

    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }
    if((state != BT_STATE_HFP_INCOMMING) && (state != BT_STATE_HFP_OUTGOING) && (state != BT_STATE_HFP_CALLACTIVE)){
        return BT_STATUS_FAILED;
    }
    
    cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
    return HF_ReportMicVolume(user_bt_env.dev[dev_index].hf_chan, vol, cmd);
}

BtStatus bt_report_spk_volume(uint8_t dev_index, uint8_t vol)
{
    HfCommand *cmd;
    uint8_t state = user_bt_get_state(dev_index);

    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }
    if((state != BT_STATE_HFP_INCOMMING) && (state != BT_STATE_HFP_OUTGOING) && (state != BT_STATE_HFP_CALLACTIVE)){
        return BT_STATUS_FAILED;
    }
    
    cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
    return HF_ReportSpeakerVolume(user_bt_env.dev[dev_index].hf_chan, vol, cmd);
}

BtStatus bt_send_hf_cmd(uint8_t dev_index, const uint8_t *at_str)
{
    HfCommand *cmd;

    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }

    cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
    return HF_SendAtCommand(user_bt_env.dev[dev_index].hf_chan, (const char*)at_str, cmd);
}

BtStatus bt_enable_voice_recognition(uint8_t dev_index, uint8_t enabled)
{
    HfCommand *cmd;

    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }

    cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
    return HF_EnableVoiceRecognition(user_bt_env.dev[dev_index].hf_chan, enabled, cmd);
}

uint8_t bt_is_voice_rec_active(uint8_t dev_index)
{
    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
        return 0;
    }
    return HF_IsVoiceRecActive(user_bt_env.dev[dev_index].hf_chan); 
}

BtStatus bt_set_media_volume(uint8_t dev_index, uint8_t volume)
{
    AvrcpAdvancedPdu *pdu;
    BtStatus status;
    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].rcp_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }

    pdu = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
    pdu->parms = (uint8_t *)btdm_malloc(64);
    status = AVRCP_TgSetAbsoluteVolume(user_bt_env.dev[dev_index].rcp_chan, pdu,volume);
    if (status != BT_STATUS_PENDING) {
        btdm_free(pdu->parms);
        btdm_free(pdu);
    }
    return status;
}

BtStatus bt_get_playstatus(uint8_t dev_index)
{
    AvrcpAdvancedPdu *pdu;
    BtStatus status;
    
    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].rcp_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }
    pdu = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
    pdu->parms = (uint8_t *)btdm_malloc(64);
    status = AVRCP_CtGetPlayStatus(user_bt_env.dev[dev_index].rcp_chan, pdu);
    if (status != BT_STATUS_PENDING) {
        btdm_free(pdu->parms);
        btdm_free(pdu);
    }
    return status;
}

BtStatus bt_get_media_info(uint8_t dev_index,AvrcpMediaAttrIdMask mediaMask)
{
    AvrcpAdvancedPdu *pdu;
    BtStatus status;
    
    if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].rcp_chan == NULL)){
        return BT_STATUS_INVALID_PARM;
    }
    pdu = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
    pdu->parms = (uint8_t *)btdm_malloc(64);
    status = AVRCP_CtGetMediaInfo(user_bt_env.dev[dev_index].rcp_chan, pdu, mediaMask);
    if (status != BT_STATUS_PENDING) {
        btdm_free(pdu->parms);
        btdm_free(pdu);
    }
    return status;
}

